/*  

Cafechess Library: Cover basic chess programming needs, like GUI, Engine integration, 
FEN and PGN parsing.

                 \\|//
                 (o o)
--------------ooO-(_)-Ooo----------------------------------
Copyright (C) 2009  By Gregory A. Prentice Snoqualmie, WA
                    www.cafechess.org
                    cafechess@comcast.net
                      Oooo
--------------oooO----(  )---------------------------------
              (  )    (_/
               \_)

Microsoft Public License (Ms-PL)
               
Revisions:

Author        Date    Description:

*/
using System;
using System.Text;
using System.Text.RegularExpressions;

namespace Cafechess.Chess.General
{
  /// <summary>
  /// Represents a standard Algebraic notation chess move.
  /// </summary>
  public class ChessMove
  {
    /// <summary>
    /// Constant values used to denote white's turn.
    /// </summary>
    public const bool  WHITE=true;
    /// <summary>
    /// Constant value used to denot black's turn.
    /// </summary>
    public const bool  BLACK=false;
    /// <summary>
    /// True if white's to move
    /// </summary>
    public bool SideToMove;
    /// <summary>
    /// Did the move put the opponent in mate?
    /// </summary>
    public bool Mate;
    /// <summary>
    /// Did the move put the opponent in check.
    /// </summary>
    public bool Check;
    /// <summary>
    /// Was a piece captured?
    /// </summary>
    public bool Capture;
    /// <summary>
    /// True if we still castle king side?
    /// </summary>
    public bool CastleKingside;
    /// <summary>
    /// True if we can still castle queen side.
    /// </summary>
    public bool CastleQueenside;
    /// <summary>
    /// Move order i.e. first, or second move in the game.
    /// </summary>
    public int FullMove;
    /// <summary>
    /// White has moved and black has not
    /// </summary>
    public int HalfMove;
    /// <summary>
    /// The collection of ?! at end of moves.
    /// </summary>
    public string Nags;
    /// <summary>
    /// The destination square the piece is moving to.
    /// </summary>
    public string Move;
    /// <summary>
    /// Piece the pawn is promoting to.
    /// </summary>
    public char Promotion;
    /// <summary>
    /// The actual piece moving.
    /// </summary>
    public char Piece;
    /// <summary>
    /// Holds the row or column that specifies which piece we are moving
    /// when one of two similar pieces can move to the same square.
    /// </summary>
    public string Qualify;
    /// <summary>
    /// Holds the piece that was captured during this move.
    /// </summary>
    public Pieces Captured;
    /// <summary>
    /// Used to parse out the chess moves.
    /// </summary>
    private Regex coMoves;


//(?<Castle>([Oo0]-[Oo0]-[Oo0])|([Oo0]-[Oo0])(?<Check>[+#]?))| 
//(?<Move>(?<Piece>[KQRBN])(?<PawnQual>[a-h][1-8])(?<Capture>x)?(?<PawnQual>[a-h][1-8])(?<Check>[+#]?)) |
//(?<Move>(?<Piece>[KQRBN])(?<PawnQual>[a-h]|[1-8])(?<Capture>x)?(?<PawnQual>[a-h][1-8])(?<Check>[+#]?)) |
//(?<Move>(?<Piece>[KQRBN])(?<Capture>x)(?<PawnQual>[a-h][1-8])(?<Check>[+#]?)) |
//(?<Move>(?<Piece>[KQRBN])(?<PawnQual>[a-h][1-8])(?<Check>[+#]?))|
//(?<Move>(?<PawnQual>[a-h][1-8])(?<Check>[+#]?)(?<Promote>=[QRBN]?))

    /// <summary>
    /// Creates an instance of a chess move.
    /// </summary>
    public ChessMove()
    {
      coMoves = new Regex( // Castles
                           "([Oo0]-[Oo0]-[Oo0])?([Oo0]-[Oo0])?" +
                           // Piece
                           "([KQRBN]?)" +
                           // Piece capture
                           "(x)?" +
                           // Pawn qualifier / capture
                           "([a-h]?[1-8]?x)?" + 
                           // qualifier or pawn move
                           "([a-h]?[1-8]?)" +
                           // Move
                           "([a-h][1-8])?" +
                           // Promotion
                           "(=[QRBN])?" +
                           // Check or mate signifier
                           "([+#])?" +
                           // Nag
                           "([!?]*)?",
                           RegexOptions.Compiled);
      
      Clear();
    }

    /// <summary>
    /// Simply changes who's turn it is by complementing the SideToMove property.
    /// </summary>
    public void switchColor()
    {
      SideToMove = !SideToMove;
    }
    /// <summary>
    /// Set the move object to it's initial stabel state.
    /// </summary>
    public void Clear()
    {
      FullMove=1;
      HalfMove=0;
      ClearMove();
    }

    /// <summary>
    /// Resets our move to it's default state: white to move all other values to empty or false.
    /// </summary>
    public void ClearMove()
    {
      SideToMove=WHITE;
      Mate=false;
      Check=false;
      Capture=false;
      Nags="";
      Move="";
      Promotion=' ';
      Piece=' ';
      Qualify="";
      CastleKingside=false;
      CastleQueenside=false;
    }
    /// <summary>
    /// Breaks appart the chess move into our object
    /// for easier manipulation.
    /// </summary>
    private const int _CASTLE_Q=1, _CASTLE_K=2, _PIECE=3, _PIECEX=4, _QUALIFIEDX=5, _MOVEA=6, _MOVEB=7, _PROMOTE=8, _CHECK=9, _NAGS=10 ;
    
    /// <summary>
    /// Attempts to parse a text sting into a valid chess move based on algebraic notation.
    /// </summary>
    /// <param name="chessmove"></param>
    /// <returns></returns>
    public bool Parse(string chessmove)
    {
      Match m;
      m = coMoves.Match(chessmove);
      if(m.Success)
      {
        Clear();
        if( m.Groups[_CASTLE_K].Length > 0 )
          CastleKingside = true;
        if( m.Groups[_CASTLE_Q].Length > 0 )
          CastleQueenside = true;
        // Check to see if we have a qualified move.
        if( m.Groups[_MOVEB].Length > 0)
        {
          Move = m.Groups[_MOVEB].ToString();
          Qualify = m.Groups[_MOVEA].ToString();
        }
        // No just a simple move.
        else if(m.Groups[_MOVEA].Length > 0)
        {
          Move = m.Groups[_MOVEA].ToString();
        }
        if( m.Groups[_PIECE].Length > 0 )
        {
          Piece = m.Groups[_PIECE].ToString()[0];
          if(m.Groups[_PIECEX].Length > 0 )
            Capture = true;
        }
        else
        {
          Piece = 'P';
        }
        if( m.Groups[_QUALIFIEDX].Length > 0 )
        {
          Qualify = m.Groups[_QUALIFIEDX].ToString();
          Qualify = Qualify.Substring(0,Qualify.Length - 1);
          Capture = true;
        }
        if(m.Groups[_PROMOTE].Length > 0 )
          Promotion = m.Groups[_PROMOTE].ToString()[1];
        if(m.Groups[_CHECK].Length > 0)
        {
          
          Check = m.Groups[_CHECK].ToString()[0] == '+';
          Mate = m.Groups[_CHECK].ToString()[0] == '#';
        }
        Nags = m.Groups[_NAGS].ToString();
      }
//      print(m);
      return m.Success;    
    }
//    static void print(Match m)
//    {
//      if(m==null)return;
//      for( int i=1; i < m.Groups.Count; i++ )
//      {
//        Group g = m.Groups[i];
//        Console.WriteLine("Group"+i+"='" + g + "'");
//      }
//    }
    
    /// <summary>
    /// Converts our chess object into standard algebraic notation.
    /// </summary>
    /// <returns></returns>
    public override string ToString()
    {
      StringBuilder build = new StringBuilder();
      if( this.CastleQueenside )
      {
        build.Append("O-O-O");
      }
      else if( this.CastleKingside )
      {
        build.Append("O-O");
      }
      else
      {
        if(Piece!='P')
          build.Append(Piece);
        if(Qualify.Length > 0)
          build.Append(Qualify);
        if(Capture)
          build.Append("x");
        build.Append(Move);
        if(Promotion!=' ')
        {
          build.Append('=');
          build.Append(Promotion);
        }
      }      
      if(Check)
        build.Append('+');
      if(Mate)
        build.Append('#');
      if(Nags.Length > 0 )
        build.Append(Nags);
      return build.ToString();
    }
  }

}
